﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;

namespace VMultiAPI
{
    static class VMultiAPI
    {
        #region vmulti.dll Imports
        [DllImport("vmulti.dll")]
        private static extern bool vmulti_connect();
        [DllImport("vmulti.dll")]
        private static extern bool vmulti_disconnect();
        [DllImport("vmulti.dll")]
        public static extern bool vmulti_update_mouse(byte button, ushort x, ushort y, byte wheelPosition);
        [DllImport("vmulti.dll")]
        public static extern bool vmulti_update_relative_mouse(byte button, ushort x, ushort y, byte wheelPosition);
        [DllImport("vmulti.dll")]
        public static extern bool vmulti_update_digi(byte status, ushort x, ushort y);
        [DllImport("vmulti.dll")]
        public static extern bool vmulti_update_joystick(ushort buttons, byte hat, sbyte x, sbyte y, sbyte rx, sbyte ry, sbyte throttle);
        [DllImport("vmulti.dll")]
        public static extern bool vmulti_update_keyboard(byte shiftKeyFlags, byte[] keyCodes);
        #endregion
        public static bool Connected { get; private set; }
        static VMultiAPI()
        {
            Connected = false;
        }
        public static void Connect()
        {
            if (vmulti_connect())
            {
                Connected = true;
                return;
            }
            throw new Exception("Cannot connect VMulti");
        }
        public static void Disconnect()
        {
            vmulti_disconnect();
        }
    }

    

    public abstract class VMultiDevice
    {
        public VMultiDevice()
        {
            UpdateMethod = UpdateMethods.AutoUpdate;
        }

        public enum UpdateMethods
        {
            AutoUpdate,
            ManualUpdate
        };

        public UpdateMethods UpdateMethod { get; set; }

        protected abstract bool UpdateDevice();
        protected void AutoUpdate()
        {
            if (UpdateMethod == VMultiDevice.UpdateMethods.AutoUpdate)
                Update();
        }

        public void Update()
        {
            if (!VMultiAPI.Connected || !UpdateDevice())
                throw new Exception("Unable to update VMulti");
            
        }
    }

    public class Keyboard
        : VMultiDevice
    {
        public static KeyCode[] vMultiKeyMap = null;
        public static string[] KeyToString = null;

        static Keyboard() {
            vMultiKeyMap = new KeyCode[(int)Enum.GetValues(typeof(System.Windows.Input.Key)).Cast<System.Windows.Input.Key>().Max() + 1];
            vMultiKeyMap[(int)System.Windows.Input.Key.A] = KeyCode.KeyA;
            vMultiKeyMap[(int)System.Windows.Input.Key.B] = KeyCode.KeyB;
            vMultiKeyMap[(int)System.Windows.Input.Key.C] = KeyCode.KeyC;
            vMultiKeyMap[(int)System.Windows.Input.Key.D] = KeyCode.KeyD;
            vMultiKeyMap[(int)System.Windows.Input.Key.E] = KeyCode.KeyE;
            vMultiKeyMap[(int)System.Windows.Input.Key.F] = KeyCode.KeyF;
            vMultiKeyMap[(int)System.Windows.Input.Key.G] = KeyCode.KeyG;
            vMultiKeyMap[(int)System.Windows.Input.Key.H] = KeyCode.KeyH;
            vMultiKeyMap[(int)System.Windows.Input.Key.I] = KeyCode.KeyI;
            vMultiKeyMap[(int)System.Windows.Input.Key.J] = KeyCode.KeyJ;
            vMultiKeyMap[(int)System.Windows.Input.Key.K] = KeyCode.KeyK;
            vMultiKeyMap[(int)System.Windows.Input.Key.L] = KeyCode.KeyL;
            vMultiKeyMap[(int)System.Windows.Input.Key.M] = KeyCode.KeyM;
            vMultiKeyMap[(int)System.Windows.Input.Key.N] = KeyCode.KeyN;
            vMultiKeyMap[(int)System.Windows.Input.Key.O] = KeyCode.KeyO;
            vMultiKeyMap[(int)System.Windows.Input.Key.P] = KeyCode.KeyP;
            vMultiKeyMap[(int)System.Windows.Input.Key.Q] = KeyCode.KeyQ;
            vMultiKeyMap[(int)System.Windows.Input.Key.R] = KeyCode.KeyR;
            vMultiKeyMap[(int)System.Windows.Input.Key.S] = KeyCode.KeyS;
            vMultiKeyMap[(int)System.Windows.Input.Key.T] = KeyCode.KeyT;
            vMultiKeyMap[(int)System.Windows.Input.Key.U] = KeyCode.KeyU;
            vMultiKeyMap[(int)System.Windows.Input.Key.V] = KeyCode.KeyV;
            vMultiKeyMap[(int)System.Windows.Input.Key.W] = KeyCode.KeyW;
            vMultiKeyMap[(int)System.Windows.Input.Key.X] = KeyCode.KeyX;
            vMultiKeyMap[(int)System.Windows.Input.Key.Y] = KeyCode.KeyY;
            vMultiKeyMap[(int)System.Windows.Input.Key.Z] = KeyCode.KeyZ;
            
            vMultiKeyMap[(int)System.Windows.Input.Key.D1] = KeyCode.Key1;
            vMultiKeyMap[(int)System.Windows.Input.Key.D2] = KeyCode.Key2;
            vMultiKeyMap[(int)System.Windows.Input.Key.D3] = KeyCode.Key3;
            vMultiKeyMap[(int)System.Windows.Input.Key.D4] = KeyCode.Key4;
            vMultiKeyMap[(int)System.Windows.Input.Key.D5] = KeyCode.Key5;
            vMultiKeyMap[(int)System.Windows.Input.Key.D6] = KeyCode.Key6;
            vMultiKeyMap[(int)System.Windows.Input.Key.D7] = KeyCode.Key7;
            vMultiKeyMap[(int)System.Windows.Input.Key.D8] = KeyCode.Key8;
            vMultiKeyMap[(int)System.Windows.Input.Key.D9] = KeyCode.Key9;
            vMultiKeyMap[(int)System.Windows.Input.Key.D0] = KeyCode.Key0;

            vMultiKeyMap[(int)System.Windows.Input.Key.Enter] = KeyCode.KeyEnter;
            vMultiKeyMap[(int)System.Windows.Input.Key.Escape] = KeyCode.KeyEscape;
            vMultiKeyMap[(int)System.Windows.Input.Key.Back] = KeyCode.KeyBackspace;
            vMultiKeyMap[(int)System.Windows.Input.Key.Tab] = KeyCode.KeyTab;
            vMultiKeyMap[(int)System.Windows.Input.Key.Space] = KeyCode.KeySpace;

            //KeyDash
            vMultiKeyMap[(int)System.Windows.Input.Key.OemMinus] = KeyCode.KeyDash;
            //Keyequal
            vMultiKeyMap[(int)System.Windows.Input.Key.OemPlus] = KeyCode.KeyEqual;
            vMultiKeyMap[(int)System.Windows.Input.Key.OemOpenBrackets] = KeyCode.KeyLeftBracket;
            vMultiKeyMap[(int)System.Windows.Input.Key.Oem6] = KeyCode.KeyRightBracket;
            vMultiKeyMap[(int)System.Windows.Input.Key.OemBackslash] = KeyCode.KeyBackSlash;
            //KeyUnused1
            vMultiKeyMap[(int)System.Windows.Input.Key.D0] = KeyCode.KeyUnused1;
            vMultiKeyMap[(int)System.Windows.Input.Key.Oem1] = KeyCode.KeySemicolon;
            vMultiKeyMap[(int)System.Windows.Input.Key.OemQuotes] = KeyCode.KeyQuotation;
            //KeyTlide
            vMultiKeyMap[(int)System.Windows.Input.Key.Oem3] = KeyCode.KeyTlide;
            vMultiKeyMap[(int)System.Windows.Input.Key.OemComma] = KeyCode.KeyComma;
            vMultiKeyMap[(int)System.Windows.Input.Key.OemPeriod] = KeyCode.KeyPeriod;

            //////
            vMultiKeyMap[(int)System.Windows.Input.Key.OemQuestion] = KeyCode.KeySlash;
            vMultiKeyMap[(int)System.Windows.Input.Key.Capital] = KeyCode.KeyCapsLock;
            //function keys
            vMultiKeyMap[(int)System.Windows.Input.Key.F1] = KeyCode.KeyF1;
            vMultiKeyMap[(int)System.Windows.Input.Key.F2] = KeyCode.KeyF2;
            vMultiKeyMap[(int)System.Windows.Input.Key.F3] = KeyCode.KeyF3;
            vMultiKeyMap[(int)System.Windows.Input.Key.F4] = KeyCode.KeyF4;
            vMultiKeyMap[(int)System.Windows.Input.Key.F5] = KeyCode.KeyF5;
            vMultiKeyMap[(int)System.Windows.Input.Key.F6] = KeyCode.KeyF6;
            vMultiKeyMap[(int)System.Windows.Input.Key.F7] = KeyCode.KeyF7;
            vMultiKeyMap[(int)System.Windows.Input.Key.F8] = KeyCode.KeyF8;
            vMultiKeyMap[(int)System.Windows.Input.Key.F9] = KeyCode.KeyF9;
            vMultiKeyMap[(int)System.Windows.Input.Key.F10] = KeyCode.KeyF10;
            vMultiKeyMap[(int)System.Windows.Input.Key.F11] = KeyCode.KeyF11;
            vMultiKeyMap[(int)System.Windows.Input.Key.F12] = KeyCode.KeyF12;

            vMultiKeyMap[(int)System.Windows.Input.Key.Snapshot] = KeyCode.KeyPrintScreen;
            vMultiKeyMap[(int)System.Windows.Input.Key.Scroll] = KeyCode.KeyScrollLock;
            vMultiKeyMap[(int)System.Windows.Input.Key.Pause] = KeyCode.KeyPause;

            vMultiKeyMap[(int)System.Windows.Input.Key.Insert] = KeyCode.KeyInsert;
            vMultiKeyMap[(int)System.Windows.Input.Key.Delete] = KeyCode.KeyDelete;
            vMultiKeyMap[(int)System.Windows.Input.Key.Home] = KeyCode.KeyHome;
            vMultiKeyMap[(int)System.Windows.Input.Key.End] = KeyCode.KeyEnd;
            vMultiKeyMap[(int)System.Windows.Input.Key.PageUp] = KeyCode.KeyPageUp;
            vMultiKeyMap[(int)System.Windows.Input.Key.Next] = KeyCode.KeyPageDown;

            vMultiKeyMap[(int)System.Windows.Input.Key.Right] = KeyCode.KeyRightArrow;
            vMultiKeyMap[(int)System.Windows.Input.Key.Left] = KeyCode.KeyLeftArrow;
            vMultiKeyMap[(int)System.Windows.Input.Key.Up] = KeyCode.KeyUpArrow;
            vMultiKeyMap[(int)System.Windows.Input.Key.Down] = KeyCode.KeyDownArrow;

            vMultiKeyMap[(int)System.Windows.Input.Key.NumLock] = KeyCode.KeyNumLock;
            vMultiKeyMap[(int)System.Windows.Input.Key.Divide] = KeyCode.KeyNumSlash;
            vMultiKeyMap[(int)System.Windows.Input.Key.Multiply] = KeyCode.KeyNumAsterisk;
            vMultiKeyMap[(int)System.Windows.Input.Key.Subtract] = KeyCode.KeyNumDash;

            vMultiKeyMap[(int)System.Windows.Input.Key.Add] = KeyCode.KeyNumAdd;
            vMultiKeyMap[(int)System.Windows.Input.Key.Return] = KeyCode.KeyNumEnter;

            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad1] = KeyCode.KeyNum1;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad2] = KeyCode.KeyNum2;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad3] = KeyCode.KeyNum3;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad4] = KeyCode.KeyNum4;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad5] = KeyCode.KeyNum5;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad6] = KeyCode.KeyNum6;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad7] = KeyCode.KeyNum7;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad8] = KeyCode.KeyNum8;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad9] = KeyCode.KeyNum9;
            vMultiKeyMap[(int)System.Windows.Input.Key.NumPad0] = KeyCode.KeyNum0;

            vMultiKeyMap[(int)System.Windows.Input.Key.Decimal] = KeyCode.KeyNumDot;
            vMultiKeyMap[(int)System.Windows.Input.Key.LeftCtrl] = KeyCode.KeyLeftControl;
            vMultiKeyMap[(int)System.Windows.Input.Key.LeftShift] = KeyCode.KeyLeftShift;
            vMultiKeyMap[(int)System.Windows.Input.Key.System] = KeyCode.KeyLeftAlt;
            vMultiKeyMap[(int)System.Windows.Input.Key.LWin] = KeyCode.KeyLeftWindows;
            vMultiKeyMap[(int)System.Windows.Input.Key.RightCtrl] = KeyCode.KeyRightControl;
            vMultiKeyMap[(int)System.Windows.Input.Key.RWin] = KeyCode.KeyRightWindows;



            KeyToString = new string[(int)Enum.GetValues(typeof(KeyCode)).Cast<KeyCode>().Max() + 1];
            KeyToString[(int)KeyCode.KeyA] = "A";
            KeyToString[(int)KeyCode.KeyB] = "B";
            KeyToString[(int)KeyCode.KeyC] = "C";
            KeyToString[(int)KeyCode.KeyD] = "D";
            KeyToString[(int)KeyCode.KeyE] = "E";
            KeyToString[(int)KeyCode.KeyF] = "F";
            KeyToString[(int)KeyCode.KeyG] = "G";
            KeyToString[(int)KeyCode.KeyH] = "H";
            KeyToString[(int)KeyCode.KeyI] = "I";
            KeyToString[(int)KeyCode.KeyJ] = "J";
            KeyToString[(int)KeyCode.KeyK] = "K";
            KeyToString[(int)KeyCode.KeyL] = "L";
            KeyToString[(int)KeyCode.KeyM] = "M";
            KeyToString[(int)KeyCode.KeyN] = "N";
            KeyToString[(int)KeyCode.KeyO] = "O";
            KeyToString[(int)KeyCode.KeyP] = "P";
            KeyToString[(int)KeyCode.KeyQ] = "Q";
            KeyToString[(int)KeyCode.KeyR] = "R";
            KeyToString[(int)KeyCode.KeyS] = "S";
            KeyToString[(int)KeyCode.KeyT] = "T";
            KeyToString[(int)KeyCode.KeyU] = "U";
            KeyToString[(int)KeyCode.KeyV] = "V";
            KeyToString[(int)KeyCode.KeyW] = "W";
            KeyToString[(int)KeyCode.KeyX] = "X";
            KeyToString[(int)KeyCode.KeyY] = "Y";
            KeyToString[(int)KeyCode.KeyZ] = "Z";

            KeyToString[(int)KeyCode.Key1] = "1";
            KeyToString[(int)KeyCode.Key2] = "2";
            KeyToString[(int)KeyCode.Key3] = "3";
            KeyToString[(int)KeyCode.Key4] = "4";
            KeyToString[(int)KeyCode.Key5] = "5";
            KeyToString[(int)KeyCode.Key6] = "6";
            KeyToString[(int)KeyCode.Key7] = "7";
            KeyToString[(int)KeyCode.Key8] = "8";
            KeyToString[(int)KeyCode.Key9] = "9";
            KeyToString[(int)KeyCode.Key0] = "0";

            KeyToString[(int)KeyCode.KeyEnter] = "Enter";
            KeyToString[(int)KeyCode.KeyEscape] = "Escape";
            KeyToString[(int)KeyCode.KeyBackspace] = "Back";
            KeyToString[(int)KeyCode.KeyTab] = "Tab";
            KeyToString[(int)KeyCode.KeySpace] = "Space";

            //KeyDash
            KeyToString[(int)KeyCode.KeyDash] = "-";
            //Keyequal
            KeyToString[(int)KeyCode.KeyEqual] = "=";
            KeyToString[(int)KeyCode.KeyLeftBracket] = "(";
            KeyToString[(int)KeyCode.KeyRightBracket] = ")";
            KeyToString[(int)KeyCode.KeyBackSlash] = "\\";
            //KeyUnused1
            //KeyToString[(int)KeyCode.KeyD0] = KeyCode.KeyUnused1;
            KeyToString[(int)KeyCode.KeySemicolon] = ";";
            KeyToString[(int)KeyCode.KeyQuotation] = "\"";
            //KeyTlide
            KeyToString[(int)KeyCode.KeyTlide] = "Tlide";
            KeyToString[(int)KeyCode.KeyComma] = ",";
            KeyToString[(int)KeyCode.KeyPeriod] = ".";

            //////
            KeyToString[(int)KeyCode.KeySlash] = "/";
            KeyToString[(int)KeyCode.KeyCapsLock] = "CapsLock";
            //function keys
            KeyToString[(int)KeyCode.KeyF1] = "F1";
            KeyToString[(int)KeyCode.KeyF2] = "F2";
            KeyToString[(int)KeyCode.KeyF3] = "F3";
            KeyToString[(int)KeyCode.KeyF4] = "F4";
            KeyToString[(int)KeyCode.KeyF5] = "F5";
            KeyToString[(int)KeyCode.KeyF6] = "F6";
            KeyToString[(int)KeyCode.KeyF7] = "F7";
            KeyToString[(int)KeyCode.KeyF8] = "F8";
            KeyToString[(int)KeyCode.KeyF9] = "F9 ";
            KeyToString[(int)KeyCode.KeyF10] = "F10";
            KeyToString[(int)KeyCode.KeyF11] = "F11";
            KeyToString[(int)KeyCode.KeyF12] = "F12";

            KeyToString[(int)KeyCode.KeyPrintScreen] = "PrintScr";
            KeyToString[(int)KeyCode.KeyScrollLock] = "Scrolllock";
            KeyToString[(int)KeyCode.KeyPause] = "Pause";

            KeyToString[(int)KeyCode.KeyInsert] = "Insert";
            KeyToString[(int)KeyCode.KeyDelete] = "Delete";
            KeyToString[(int)KeyCode.KeyHome] = "Home";
            KeyToString[(int)KeyCode.KeyEnd] = "End";
            KeyToString[(int)KeyCode.KeyPageUp] = "PgUp";
            KeyToString[(int)KeyCode.KeyPageDown] = "PgDn";

            KeyToString[(int)KeyCode.KeyRightArrow] = "→";
            KeyToString[(int)KeyCode.KeyLeftArrow] = "←";
            KeyToString[(int)KeyCode.KeyUpArrow] = "↑";
            KeyToString[(int)KeyCode.KeyDownArrow] = "↓";

            KeyToString[(int)KeyCode.KeyNumLock] = "NumLock";
            KeyToString[(int)KeyCode.KeyNumSlash] = "NumSlash";
            KeyToString[(int)KeyCode.KeyNumAsterisk] = "*";
            KeyToString[(int)KeyCode.KeyNumDash] = "-";

            KeyToString[(int)KeyCode.KeyNumAdd] = "+";
            KeyToString[(int)KeyCode.KeyNumEnter] = "NumEnter";

            KeyToString[(int)KeyCode.KeyNum1] = "key1";
            KeyToString[(int)KeyCode.KeyNum2] = "key2";
            KeyToString[(int)KeyCode.KeyNum3] = "key3";
            KeyToString[(int)KeyCode.KeyNum4] = "key4";
            KeyToString[(int)KeyCode.KeyNum5] = "key5";
            KeyToString[(int)KeyCode.KeyNum6] = "key6";
            KeyToString[(int)KeyCode.KeyNum7] = "key7";
            KeyToString[(int)KeyCode.KeyNum8] = "key8";
            KeyToString[(int)KeyCode.KeyNum9] = "key9";
            KeyToString[(int)KeyCode.KeyNum0] = "key0";

            KeyToString[(int)KeyCode.KeyNumDot] = ".";
            KeyToString[(int)KeyCode.KeyLeftControl] = "lctl";
            KeyToString[(int)KeyCode.KeyLeftShift] = "lshift";
            KeyToString[(int)KeyCode.KeyLeftAlt] = "lalt";
            KeyToString[(int)KeyCode.KeyLeftWindows] = "lwin";
            KeyToString[(int)KeyCode.KeyRightControl] = "rctl";
            KeyToString[(int)KeyCode.KeyRightWindows] = "rwin";
        }
                                 
        public class Key
        {
            internal Key(Keyboard parent)
            {
                _parent = parent;
            }
            private Keyboard _parent;
            private bool _pressed = false;
            public bool Pressed
            {
                get { return _pressed; }
                set { _pressed = value; _parent.AutoUpdate(); }
            }
            public void Press()
            {
                _pressed = true;
                _parent.Update();
                _pressed = false;
                _parent.Update();
            }
        }

        public enum KeyCode
        {
            KeyA = 0x4,
            KeyB,KeyC,KeyD,KeyE,KeyF,
            KeyG,KeyH,KeyI,KeyJ,KeyK,
            KeyL,KeyM,KeyN,KeyO,KeyP,
            KeyQ,KeyR,KeyS,KeyT,KeyU,
            KeyV,KeyW,KeyX,KeyY,KeyZ,
            Key1,Key2,Key3,Key4,Key5,
            Key6,Key7,Key8,Key9,Key0,
            KeyEnter,KeyEscape,KeyBackspace,
            KeyTab,KeySpace,
            /// <summary>
            /// - and _
            /// </summary>
            KeyDash,
            /// <summary>
            /// = and +
            /// </summary>
            KeyEqual,
            /// <summary>
            /// [ and {
            /// </summary>
            KeyLeftBracket,
            /// <summary>
            /// ] and }
            /// </summary>
            KeyRightBracket,
            /// <summary>
            /// \ and |
            /// </summary>
            KeyBackSlash,KeyUnused1,
            /// <summary>
            /// ; and :
            /// </summary>
            KeySemicolon,
            /// <summary>
            /// ' and "
            /// </summary>
            KeyQuotation,
            /// <summary>
            /// ~ and `
            /// </summary>
            KeyTlide,
            /// <summary>
            /// , and <
            /// </summary>
            KeyComma,
            /// <summary>
            /// . and >
            /// </summary>
            KeyPeriod,
            /// <summary>
            /// / and ?
            /// </summary>
            KeySlash,
            KeyCapsLock,
            KeyF1, KeyF2, KeyF3, KeyF4,
            KeyF5, KeyF6, KeyF7, KeyF8,
            KeyF9, KeyF10, KeyF11, KeyF12,
            KeyPrintScreen,KeyScrollLock,KeyPause,
            KeyInsert,KeyHome,KeyPageUp,KeyDelete,
            KeyEnd, KeyPageDown,
            KeyRightArrow,KeyLeftArrow,KeyDownArrow,KeyUpArrow,
            KeyNumLock,KeyNumSlash,KeyNumAsterisk,KeyNumDash,
            KeyNumAdd,KeyNumEnter,KeyNum1,KeyNum2,KeyNum3,KeyNum4,
            KeyNum5,KeyNum6,KeyNum7,KeyNum8,KeyNum9,KeyNum0,
            KeyNumDot,
            KeyLeftControl = 0xE0,
            KeyLeftShift,
            KeyLeftAlt,
            KeyLeftWindows = 0xE3, 
            KeyRightControl,
            KeyRightShift,
            KeyRightAlt,
            KeyRightWindows = 0xE7,
        };


        internal Keyboard()
        {
            keys = new Key[maxKeys];
            for (int i = 0; i < maxKeys; ++i)
                keys[i] = new Key(this);
        }

        public Key GetKey(KeyCode keyCode)
        {
            return keys[(int)keyCode];
        }
        protected override bool UpdateDevice()
        {
            byte[] keyPressed = new byte[6];
            byte shiftKeys = 0;
            int numPressed = 0;
            for (int i = 0; i <= (int)KeyCode.KeyNumDot; ++i)
            {
                if (keys[i].Pressed)
                {
                    keyPressed[numPressed] = (byte)i;
                    numPressed++;
                }
                if (numPressed >= 6) break;
            }
            for (int i = 0; i <= (int)KeyCode.KeyRightWindows - (int)KeyCode.KeyLeftControl;
                ++i)
            {
                if (keys[(int)KeyCode.KeyLeftControl + i].Pressed)
                {
                    shiftKeys += (byte)(1 << i);
                }
            }
            return VMultiAPI.vmulti_update_keyboard(shiftKeys, keyPressed);
        }

        private Key[] keys;
        private const int maxKeys = 0xE8;

    }

    public class Joystick
        : VMultiDevice
    {
        public class Button
        {
            internal Button(Joystick parent)
            {
                _parent = parent;
            }
            private Joystick _parent;
            private bool _pressed = false;
            public bool Pressed
            {
                get { return _pressed; }
                set { _pressed = value; _parent.AutoUpdate(); }
            }
        }

        private byte hat;
        private sbyte x;
        private sbyte y;
        private sbyte rx;
        private sbyte ry;
        private sbyte throttle;

        public byte HatPosition { get { return hat; } set { hat = value; AutoUpdate(); } }
        public sbyte X { get { return x; } set { x = value; AutoUpdate(); } }
        public sbyte Y { get { return y; } set { y = value; AutoUpdate(); } }
        public sbyte RotateX { get { return rx; } set { rx = value; AutoUpdate(); } }
        public sbyte RotateY { get { return ry; } set { ry = value; AutoUpdate(); } }
        public sbyte Throttle { get { return throttle; } set { throttle = value; AutoUpdate(); } }
        
        public Button[] Buttons { get; private set; }

        internal Joystick()
        {
            x = y = rx = ry = 0;
            hat = 0;
            throttle = -127;
            Buttons = new Button[16];
            for (int i = 0; i < 16; ++i)
                Buttons[i] = new Button(this);
        }

        protected override bool UpdateDevice()
        {
            ushort buttonStatus = 0;
            for (int i = 0; i < 16; ++i)
                if (Buttons[i].Pressed)
                    buttonStatus |= (byte)(1 << i);
            return VMultiAPI.vmulti_update_joystick(buttonStatus, hat, x, y, rx, ry, throttle);
        }
    }

    class VMulti
    {
        static VMulti _instance;

        public Joystick Joystick { get; private set; }
        public Keyboard Keyboard { get; private set; }

        private VMulti()
        {
            try
            {
                VMultiAPI.Connect();
            }
            catch (Exception e)
            {
                throw e;
            }
            Joystick = new Joystick();
            Keyboard = new Keyboard();
        }

        public static VMulti GetInstance()
        {
            if (_instance == null)
            {
                try
                {
                    _instance = new VMulti();
                }
                catch (Exception e)
                {
                    return null;
                }
            }
            return _instance;
        }

        internal void Reset()
        {
            byte[] keyCodes = new byte[6];
            for (int i = 0; i < 6; ++i)
                keyCodes[i] = 0;
            VMultiAPI.vmulti_update_joystick(0, 0, 0, 0, 0, 0, 0);
            VMultiAPI.vmulti_update_keyboard(0, keyCodes);

        }
    }
}
